/***************************************************************************
 *   Copyright (C) 2009 by Zheng Yonglei
 *   zhengyonglei@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <fstream>
#include <sstream>
#include <iomanip>
#include <cassert>

#include <time.h>
#include <stdlib.h>

#include <QMessageBox>
#include <QFont>

#include "WordManager.h"

WordManager::WordManager():
	mCurWordID(-1),
	mCurWordIndex(-1),
	mLastWordID(-1),
	mCurCandidatesType(WS_Reciting),
	mTotalReciteCount(0),
	mReciteTestMethod(yes_no_method),
	mCorrectChoiceIndex(-1),
	mSelectedChoice(-1),
	mAssociateRemainCycle(0)
{
	WLOG<<"WordManager::WordManager()-->A WordManager constructed."<<endl;
	mPathSeparator = QDir::separator();

	mReviewCycle = 20;
	mReviewFrequency = 20;

	//mDictList.clear();
	mUnhitReviewingWords.clear();
	mResumeWords.clear();
	mResumeUnhitRevWords.clear();

	mCurTaskElapseTime = QTime(0,0,0).toString();

	readSettings();
	loadAssiciateMap();
	//resetRecitingInfoOnDatabase();
	srand((int)time(0));
}

WordManager::~WordManager()
{
	writeSettings();
	finishLastTask();
}

void WordManager::resetRecitingInfoOnDatabase()
{
	QString queryCommand;
	//update reciting words status to is_active = false
	queryCommand = "update history set is_active = 0 ";
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);

	queryCommand = QString("update history set last_reciting_status=%1 ").arg(int(WS_Finished));
	queryCommand.append(QString("where last_reciting_status=%1;").arg(int(WS_Reviewing)));
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);
}

void WordManager::readSettings()
{
}

void WordManager::loadUnfinishedTask()
{
	cout<<"WordManager::loadUnfinishedTask"<<endl;
	if(!DB.mUsrMain.isEmpty())
	{
		QString file = QString("./data/users/") + DB.mUsrMain + QString("/") + DB.mUsrMain + QString(".conf");
		//QSettings settings("./configure/ReciteGREWords.conf" , QSettings::IniFormat );
		QSettings settings(file , QSettings::IniFormat );
		settings.beginGroup(QString("%1").arg(OPT.lastAccount));
		QVariant val;
		val = settings.value("WordsToResume");
		if(val.isValid())
		{
			cout<<"WordManager::readSettings-->WordsToResume = "<<val.toString().toUtf8().data()<<endl;
			QStringList wordsToResume = val.toString().split("|", QString::SkipEmptyParts);
			for(int i=0; i<wordsToResume.size(); i++)
			{
				int id = wordsToResume[i].toInt();
				mResumeWords.push_back(id);
			}
		}

		val = settings.value("UnhitWordsToResume");
		if(val.isValid())
		{
			cout<<"WordManager::readSettings-->UnhitWordsToResume = "<<val.toString().toUtf8().data()<<endl;
			QStringList unhitWordsToResume = val.toString().split("|", QString::SkipEmptyParts);
			for(int i=0; i<unhitWordsToResume.size(); i++)
			{
				int id = unhitWordsToResume[i].toInt();
				mResumeUnhitRevWords.push_back(id);
			}
		}

		val = settings.value("CurTaskElapseTime");
		if(val.isValid()) mCurTaskElapseTime = val.toString();
		settings.endGroup();
	}
}

void WordManager::writeSettings()
{
	if(!DB.mUsrMain.isEmpty() && !OPT.lastAccount.isEmpty())
	{
		//cout<<"WordManager::writeSettings"<<endl;
		QString file = QString("./data/users/") + DB.mUsrMain + QString("/") + DB.mUsrMain + QString(".conf");
		//QSettings settings("./configure/ReciteGREWords.conf" , QSettings::IniFormat );
		QSettings settings(file , QSettings::IniFormat );

		settings.beginGroup(QString("%1").arg(OPT.lastAccount));
		if (!mWordsVector.isEmpty())
		{
			QString wordsToResume;
			//wordsToResume.clear();
			for(int i=0; i<mWordsVector.size(); i++)
			{
				int id = mWordsVector[i];
				wordsToResume.append(QString("%1").arg(id));
				if(i < mWordsVector.size()-1)
					wordsToResume.append('|');
			}
			settings.setValue("WordsToResume", wordsToResume);
		}

		if (!mUnhitReviewingWords.isEmpty())
		{
			QString unhitWordsToResume;
			//unhitWordsToResume.clear();
			for(int i=0; i<mUnhitReviewingWords.size(); i++)
			{
				int id = mUnhitReviewingWords[i];
				unhitWordsToResume.append(QString("%1").arg(id));
				if(i < mUnhitReviewingWords.size()-1)
					unhitWordsToResume.append('|');
			}
			settings.setValue("UnhitWordsToResume", unhitWordsToResume);
		}

		settings.setValue("CurTaskElapseTime", mCurTaskElapseTime);
		settings.endGroup();
	}
}

void WordManager::setTask(WordManagerTask task)
{
	cout<<"WordManager::setTask"<<endl;
	//finishLastTask();
	//clear previous tasks
	mCurTask = task;
	mReviewCycle = 20;
	mReviewFrequency = 20;
	mCurWordID = -1;
	mCurWordIndex = -1;

	switch(mCurTask.taskType)
	{
	case TaskType_Browse:
		prepareToBrowseWords();
		break;
	case TaskType_Recite:
		prepareToReciteWords();
		break;
	case TaskType_None:
		return;
	default:
		cout<<"WordManager::setTask-->Invalid task type."<<endl;
		return;
	}
}
void WordManager::prepareToReciteWords()
{
	//WLOG<<"WordManager::prepareToReciteWords"<<endl;
	switch(mCurTask.selectWordMethod)
	{
	case select_by_list_name:
		getBasicInfoByListName();break;
	case select_by_active_status:
		getBasicInfoByActiveStatus();break;
	case select_by_resume_list:
		getBasicInfoByResumeList();break;
	default:cout<<"WordManager::prepareToBrowseWords-->Error! Invalid selectWordMethod"<<endl;
		break;
	}

	initRecitingTask();
}

void WordManager::prepareToBrowseWords()
{
	switch(mCurTask.selectWordMethod)
	{
	case select_by_list_name:
		getBasicInfoByListName();
		mCurWordIndex = 0;
		mCurWordID = mWordsVector[mCurWordIndex];
		//getCurWordRecitingInfo();
		break;
	case select_by_active_status:
		getBasicInfoByActiveStatus();
		mCurWordIndex = 0;
		mCurWordID = mWordsVector[mCurWordIndex];
		break;
	default:
		cout<<"WordManager::prepareToBrowseWords-->Error! Invalid selectWordMethod"<<endl;
		break;
	}
	initBrowseTask();
}

void WordManager::addWord(int id)
{
	mWordsVector.push_back(id);
	mWordsMap.insert(make_pair(id, Word(id)));
}

void WordManager::getBasicInfo(int id)
{
	QString queryCommand;
	queryCommand.clear();
	vector<QString>requirements;
	requirements.push_back("id");
	requirements.push_back("English");
	requirements.push_back("soundmark");
	requirements.push_back(DictNames::getDataBaseColumnName(DictNames::GRERedBible));

	queryCommand.append("select ");
	for (int i=0; i<requirements.size()-1; i++)
		queryCommand.append(requirements[i]).append(", ");
	queryCommand.append(requirements[requirements.size()-1]);
	queryCommand.append(QString(" from words where id=%1;").arg(id));
	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);

	//int id = DB.getTableValue(i, "id").toInt();
	QString English = DB.getTableValue(0, "English");
	QString soundmark = DB.getTableValue(0, "soundmark");
	QString exp_red_bible = DB.getTableValue(0, "exp_red_bible");

	assert(id == DB.getTableValue(0, "id").toInt());
	//mWordsMap[id].setID(id);
	mWordsMap[id].setEnglish(English);
	mWordsMap[id].setSoundMark(soundmark);
	mWordsMap[id].setRedBibleExp(exp_red_bible);//essential

	queryCommand = QString("update history set is_active=1 where id=%1;").arg(id);
	DB.quickUpdate(queryCommand);
}

void WordManager::getBasicInfoByResumeList()
{
	if(mResumeWords.empty())
	{
		cout<<"WordManager::getBasicInfoByResumeList-->empty mResumeWords"<<endl;
		QCoreApplication::quit();
	}

	QString queryCommand;
	queryCommand.clear();
	vector<QString>requirements;
	requirements.push_back("id");
	requirements.push_back("English");
	requirements.push_back("soundmark");
	requirements.push_back(DictNames::getDataBaseColumnName(DictNames::GRERedBible));

	queryCommand.append("select ");
	for (int i=0; i<requirements.size()-1; i++)
		queryCommand.append(requirements[i]).append(", ");
	queryCommand.append(requirements[requirements.size()-1]);
	queryCommand.append(" from words where ");
	for (int i=0; i<mResumeWords.size(); i++)
	{
		int id = mResumeWords[i];
		queryCommand.append(QString("id = %1").arg(id));
		if(i < mResumeWords.size()-1)
			queryCommand.append(" or ");
	}
	queryCommand.append(";");
	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);

	for (int i=0; i<DB.getRowCount(); i++)
	{
		int id = DB.getTableValue(i, "id").toInt();
		QString English = DB.getTableValue(i, "English");
		QString soundmark = DB.getTableValue(i, "soundmark");
		QString exp_red_bible = DB.getTableValue(i, "exp_red_bible");

		addWord(id);
		mWordsMap[id].setID(id);
		mWordsMap[id].setEnglish(English);
		mWordsMap[id].setSoundMark(soundmark);
		mWordsMap[id].setRedBibleExp(exp_red_bible);//essential
	}
}

void WordManager::getBasicInfoByActiveStatus()
{
	QString queryCommand;

	queryCommand.clear();
	vector<QString>requirements;

	requirements.push_back("words.id");
	requirements.push_back("words.English");
	requirements.push_back("words.soundmark");
	requirements.push_back("words."+ DictNames::getDataBaseColumnName(DictNames::GRERedBible));

	queryCommand.append("select ");
	for (int i=0; i<requirements.size()-1; i++)
		queryCommand.append(requirements[i]).append(", ");
	queryCommand.append(requirements[requirements.size()-1]);
	queryCommand.append(" from words,history where words.id = history.id and  history.is_active = 1;");

	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);
	for (int i=0; i<DB.getRowCount(); i++)
	{
		int id = DB.getTableValue(i, "id").toInt();
		QString English = DB.getTableValue(i, "English");
		QString soundmark = DB.getTableValue(i, "soundmark");
		QString exp_red_bible = DB.getTableValue(i, "exp_red_bible");
		addWord(id);
		mWordsMap[id].setID(id);
		mWordsMap[id].setEnglish(English);
		mWordsMap[id].setSoundMark(soundmark);
		mWordsMap[id].setRedBibleExp(exp_red_bible);//essential
	}
}

void WordManager::getBasicInfoByListName()
{
	QString listName =  mCurTask.listName;
	listName.remove("list");
	int list = listName.toInt();
	//cout<<"WordManager::getBasicInfoByListName-->list = "<<list<<endl;

	QString queryCommand;
	vector<QString>requirements;
	//all kinds of information intend to fetch from database
	requirements.push_back("words.id");
	requirements.push_back("words.English");
	requirements.push_back("words.soundmark");
	requirements.push_back("words."+ DictNames::getDataBaseColumnName(DictNames::GRERedBible));

	queryCommand.append("select ");
	for (int i=0; i<requirements.size()-1; i++)
		queryCommand.append(requirements[i]).append(", ");
	queryCommand.append(requirements[requirements.size()-1]);

	if (mCurTask.listType == red_bible_list)
	{
		//cout<<"WordManager::getBasicInfoByListName-->mCurTask.listType == red_bible_list"<<endl;
		queryCommand.append(QString(
					" from words,lists where words.id = lists.id "
					"and  lists.red_bible_list = %1;").arg(list));
	}
	else if (mCurTask.listType == custom_list)
	{
		queryCommand.append(QString(
					" from words,lists where words.id = lists.id "
					"and  lists.custom_list = %1;").arg(list));
	}
	else
	{
		cout<<"WordManager::getBasicInfoByListName-->mCurTask.listType is a invalid value"<<endl;
		QCoreApplication::quit();
	}

	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);
	//cout<<"WordManager::getBasicInfoByListName-->queryCommand = "<<queryCommand.toUtf8().data()<<endl;

	for (int i=0; i<DB.getRowCount(); i++)
	{
		//cout<<"WordManager::getBasicInfoByListName-->i = "<<i<<endl;
		int id = DB.getTableValue(i, "id").toInt();
		QString English = DB.getTableValue(i, "English");
		QString soundmark = DB.getTableValue(i, "soundmark");
		QString exp_red_bible = DB.getTableValue(i, "exp_red_bible");
		//cout<<"WordManager::getBasicInfoByListName-->id = "<<id<<endl;
		//cout<<"WordManager::getBasicInfoByListName-->English = "<<English.toUtf8().data()<<endl;
		addWord(id);
		mWordsMap[id].setID(id);
		mWordsMap[id].setEnglish(English);
		mWordsMap[id].setSoundMark(soundmark);
		mWordsMap[id].setRedBibleExp(exp_red_bible);//essential
		//cout<<"WordManager::getBasicInfoByListName-->mWordsMap["<<id<<"]:English = "<<mWordsMap[id]->getEnglish().toUtf8().data()<<endl;
	}
}

void WordManager::clearWordContainers()
{
	//cout<<"WordManager::clearWordContainers"<<endl;
	mCurWordID = -1;
	mCurWordIndex = -1;

	mWordsMap.clear();
	mWordsVector.clear();

	mRecitingWord.clear();
	mReviewingWord.clear();
	mUntouchedWord.clear();
	mFinishedWord.clear();

	mUnhitReviewingWords.clear();
	mResumeWords.clear();
	mResumeUnhitRevWords.clear();
	mTestWordHistory.clear();
	mAssociateTempVector.clear();
}

void WordManager::finishLastTask()
{
	//cout<<"WordManager::finishLastTask"<<endl;
	switch(mCurTask.taskType)
	{
	case TaskType_Browse:
		finishBrowseWords();
		break;
	case TaskType_Recite:
		finishReciteWords();
		break;
	case TaskType_None:
		break;
	default:
		cout<<"WordManager::finishLastTask-->No task to finish."<<endl;
	}
	clearWordContainers();
}

void WordManager::finishBrowseWords()
{
	if (!DB.mUsrMain.isEmpty())
	{
		cout<<"WordManager::finishBrowseWords"<<endl;
		QString queryCommand;
		queryCommand = "update history set is_active = 0; ";
		DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);
	}
}

void WordManager::finishReciteWords()
{
	cout<<"WordManager::finishReciteWords"<<endl;
	QString queryCommand;

	if (mCurWordID>0)
		updateCurWordRecitingInfo();

	//queryCommand.clear();
	queryCommand = QString("update history set last_reciting_status=%1 ").arg(int(WS_Finished));
	queryCommand.append(QString("where last_reciting_status=%1;").arg(int(WS_Reviewing)));
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);

	//queryCommand.clear();
	queryCommand = "update history set is_active=0; ";
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);

	queryCommand = "update history set frozen_remain_cycle=0; ";
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);
}

void WordManager::initBrowseTask()
{
	cout<<"WordManager::initBrowseTask"<<endl;
	QString queryCommand;

	DB.execQuery("BEGIN", QueryT_Write, DB.mUsrMain);
	for(int i=0; i<mWordsVector.size(); i++)
	{
		int wordID = mWordsVector[i];
		mResumeWords.push_back(wordID);

		queryCommand.clear();
		queryCommand.append(QString("update history set is_active = 1 where id = %1;").arg(wordID));
		DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);
	}
	DB.execQuery("COMMIT", QueryT_Write, DB.mUsrMain);
}

void WordManager::initRecitingTask()
{
	cout<<"WordManager::initRecitingTask"<<endl;
	//update reciting words status to is_active = true
	QString queryCommand;
	queryCommand = "update history set is_active = 0;";
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);

	mResumeWords.clear();
	mUnhitReviewingWords.clear();
	cout<<"WordManager::initRecitingTask-->mWordsVector.size() = "<<mWordsVector.size()<<endl;
	DB.execQuery("BEGIN", QueryT_Write, DB.mUsrMain);
	for(int i=0; i<mWordsVector.size(); i++)
	{
		int wordID = mWordsVector[i];
		mResumeWords.push_back(wordID);
		//queryCommand.clear();
		queryCommand = QString("update history set is_active = 1 where id = %1;").arg(wordID);
		DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);
	}
	DB.execQuery("COMMIT", QueryT_Write, DB.mUsrMain);

	queryCommand.append(QString(
			"select id,last_reciting_status,difficulty_level,recite_date_history "
			"from history where is_active = 1;"));
	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);
	for(int i=0; i<mWordsVector.size(); i++)
	{
		int wordID = DB.getTableValue(i,"id").toInt();
		if (wordID != mWordsVector[i])
		{
			cout	<<"WordManager::initRecitingTask-->wordID["<<i<<"]="<<wordID
					<<" while mWordsVector["<<i<<"]="<<mWordsVector[i]<<endl;
			assert(0);
		}

		QString recite_date_history = DB.getTableValue(i,"recite_date_history");
		mWordsMap[wordID].setReciteDateHistory(recite_date_history);
	}

	queryCommand = QString("update history ");
	queryCommand.append(QString("set last_reciting_status = %1 ").arg(WS_Reviewing));
	queryCommand.append(QString("where last_reciting_status =%1 ").arg(WS_Finished));
	queryCommand.append(QString("and is_active = 1;"));
	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);

	if (mCurTask.selectWordMethod == select_by_resume_list)
	{
		//cout<<"WordManager::initRecitingTask-->mCurTask.selectWordMethod == select_by_resume_list"<<endl;
		mUnhitReviewingWords.clear();
		for (int i=0; i<mResumeUnhitRevWords.size(); i++)
			mUnhitReviewingWords.push_back(mResumeUnhitRevWords[i]);
	}
	else
	{
		//cout<<"WordManager::initRecitingTask-->mCurTask.selectWordMethod != select_by_resume_list"<<endl;
		queryCommand = QString("select id from history where is_active=1 and (last_reciting_status=%1 ").arg(int(WS_Finished));
		queryCommand.append(QString(" or last_reciting_status=%1);").arg(int(WS_Reviewing)));
		DB.quickGet(queryCommand);
		//cout<<"queryCommand="<<queryCommand.toUtf8().data()<<endl;
		for (int i=0; i<DB.getRowCount(); i++)
		{
			//cout<<"i="<<i<<endl;
			int id = DB.getTableValue(i,"id").toInt();
			//cout<<"id="<<id<<endl;
			mUnhitReviewingWords.push_back(id);
		}
	}
}

bool WordManager::reciteNextWord()
{
	cout<<"WordManager::reciteNextWord"<<endl;
	QString queryCommand;

	if (mCurWordID>0)
		updateCurWordRecitingInfo();

	updateStatusCount();
	selectCandidatesType();

	mCurWordID = selectRecitingWord();

	if(mCurWordID < 0 && mCurCandidatesType == WS_Reciting)
	{
		updateWordFrozenInfo();
		mCurCandidatesType = WS_Reviewing;
		cout<<"WordManager::reciteNextWord-->force set mCurCandidatesType to WS_Reviewing"<<endl;
		mCurWordID = selectRecitingWord();
	}
	else if(mCurWordID < 0 && mCurCandidatesType == WS_Reviewing)
	{
		updateWordFrozenInfo();
		mCurCandidatesType = WS_Reciting;
		cout<<"WordManager::reciteNextWord-->force set mCurCandidatesType to WS_Reciting"<<endl;
		mCurWordID = selectRecitingWord();
	}
	else if(mCurWordID < 0 && mCurCandidatesType == WS_Associate)
	{
		//updateWordFrozenInfo();
		mAssociateRemainCycle=0;
		mAssociateTempVector.clear();
		mCurCandidatesType = WS_Reviewing;
		cout<<"WordManager::reciteNextWord-->force set mCurCandidatesType to WS_Reviewing"<<endl;
		mCurWordID = selectRecitingWord();
	}

	cout<<"WordManager::reciteNextWord-->mCurWordID = "<<mCurWordID<<endl;
	if(mCurWordID < 0)
	{
		cout	<<"WordManager::reciteNextWord-->Invalid mCurWordID = "<<mCurWordID
				<<". Try to reset frozen_remain_cycle values to solve this problem."<<endl;
		//return false;
		//assert(0);
		vector<int> validWords;
		for (int i=0; i<mRecitingWord.size(); i++)
			if (mRecitingWord[i] != mLastWordID)
				validWords.push_back(mRecitingWord[i]);
		for (int i=0; i<mReviewingWord.size(); i++)
			if (mRecitingWord[i] != mLastWordID)
				validWords.push_back(mReviewingWord[i]);

		QString command = QString("update history set frozen_remain_cycle=0 where ");
		for (int i=0; i<validWords.size()-1; i++)
			command.append(QString(" id=%1 or ").arg(validWords[i]));
		command.append(QString(" id=%1;").arg(validWords[validWords.size()-1]));
		DB.quickUpdate(command);

		mCurWordID = selectRecitingWord();
	}

	if(mCurWordID < 0)
	{
		cout	<<"WordManager::reciteNextWord-->Fatal error: Invalid mCurWordID = "<<mCurWordID
				<<" after reset frozen_remain_cycle values."<<endl;
		//return false;
		assert(0);
	}
	else
	{
		mCorrectChoiceIndex = -1;
		mSelectedChoice = -1;

		int index = mUnhitReviewingWords.indexOf(mCurWordID);
		if (index != -1)
			mUnhitReviewingWords.remove(index);

		WLOG<<STARS<<endl;
		WLOG<<"WordManager::reciteNextWord-->reciting words count is :"<<mRecitingWord.size()<<endl;
		WLOG<<"WordManager::reciteNextWord-->unhit words count is :"<<mUnhitReviewingWords.size()<<endl;
		WLOG<<"WordManager::reciteNextWord-->reciting words are:"<<endl;
		for (int i=0; i<mRecitingWord.size(); i++)
		{
			int id = mRecitingWord[i];
			Word* word = getWord(id);
			if(word)
				WLOG<<"word["<<id<<"]"<<word->getEnglish().toUtf8().data()<<"\t";
			else
			{
				cout<<"WordManager::reciteNextWord-->Has no information about word(id = "<<id<<")."<<endl;
				QCoreApplication::quit();
			}
		}
		WLOG<<endl;
		WLOG<<"Current word ID is "<<mCurWordID<<endl;
		WLOG<<STARS<<endl;

		getCurWordRecitingInfo();
		setReciteTestMode();
		updateWordFrozenInfo();
		if (mCurCandidatesType == WS_Associate)
			initActiveWordTotalReciteCount();
		mLastWordID = mCurWordID;
		mTestWordHistory.push_back(mCurWordID);

		return true;
	}
}

int WordManager::selectRecitingWord()
{
	//cout<<"WordManager::selectRecitingWord"<<endl;
	QString queryCommand;
	//QString status;
	QVector<int>candidates;

	if(mCurCandidatesType == WS_Reviewing && mUnhitReviewingWords.size() > 0)
	{
		cout<<"WordManager::selectRecitingWord-->select unhit words."<<endl;
		for(int i=0; i<mUnhitReviewingWords.size(); i++)
		{
			int id = mUnhitReviewingWords[i];
			candidates.push_back(id);
		}
	}
	else if (mCurCandidatesType == WS_Associate && OPT.enableAssociateMap)
	{
		if (mAssociateTempVector.size() > 0)
		{
			//QVector<int> wordsToRemove;
			queryCommand = QString("select id,difficulty_level,last_reciting_status from history where ");
			for (int i=0; i<mAssociateTempVector.size()-1; i++)
			{
				queryCommand.append(QString(" id=%1 or ").arg(mAssociateTempVector[i].id));
				//if (mWordsVector.indexOf(mAssociateTempVector[i]) == -1)
					//candidates.push_back(mAssociateTempVector[i]);
			}
			queryCommand.append(QString(" id=%1;").arg(mAssociateTempVector[mAssociateTempVector.size()-1].id));
			//cout<<"WordManager::selectRecitingWord-->select associate words. mAssociateTempVector.size()="<<mAssociateTempVector.size()<<endl;
			DB.quickGet(queryCommand);
			for (int i=0; i<DB.getRowCount(); i++)
			{
				int id = DB.getTableValue(i, "id").toInt();
				int diffLevel = DB.getTableValue(i, "difficulty_level").toInt();
				WordStatus status = WordStatus(DB.getTableValue(i, "last_reciting_status").toInt());
				if (mWordsVector.indexOf(id) == -1 && (diffLevel>=10 || status == WS_Untouched))
					for (int j=0; j<mAssociateTempVector.size(); j++)
						if (id == mAssociateTempVector[j].id)
							for (int k=0; k<mAssociateTempVector[j].conWeight; k++)
								candidates.push_back(id);
			}
		}
	}
	else
	{
		cout<<"WordManager::selectRecitingWord-->select reciting or hit words."<<endl;
		queryCommand.clear();
		queryCommand.append(QString(""
				"select id,difficulty_level,frozen_remain_cycle "
				"from history "
				"where is_active = 1 and last_reciting_status = %1;").arg(int(mCurCandidatesType)));
		DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);
		//WLOG<<"WordManager::selectRecitingWord-->queryCommand = "<<queryCommand.toUtf8().data()<<endl;

		//cout<<"WordManager::selectRecitingWord-->wordCount = "<<wordCount<<endl;
		for(int i=0; i<DB.getRowCount(); i++)
		{
			int id = DB.getTableValue(i,"id").toInt();
			int diffLevel = DB.getTableValue(i,"difficulty_level").toInt();
			int frozenRemainCycle = DB.getTableValue(i,"frozen_remain_cycle").toInt();
			diffLevel = diffLevel/20;
			if (diffLevel <= 0)
				diffLevel = 1;
			if (diffLevel > 10)
				diffLevel = 10;
			for(int j=0; j<diffLevel; j++)
			{
				if(frozenRemainCycle == 0)
					candidates.push_back(id);
			}
		}
	}

	cout<<"WordManager::selectRecitingWord-->candidates.size() = "<<candidates.size()<<endl;
	if(candidates.size() == 0)
		return -1;

	int resultIndex = random(candidates.size());
	//cout<<"WordManager::selectRecitingWord-->result = "<<candidates[resultIndex]<<endl;

	if (mCurCandidatesType == WS_Associate && OPT.enableAssociateMap && candidates[resultIndex] > 0)
	{
		addWord(candidates[resultIndex]);
		getBasicInfo(candidates[resultIndex]);
		//initActiveWordTotalReciteCount();
	}

	return candidates[resultIndex];
}

void WordManager::selectCandidatesType()
{
	//cout<<"WordManager::selectCandidatesType"<<endl;
	//check for new words
	if(mRecitingWord.size() <= 10 && mUntouchedWord.size() > 0)
	{
		mCurCandidatesType =  WS_Untouched;
		cout<<"WordManager::selectCandidatesType-->candidate type is WS_Untouched."<<endl;
		return;
	}

	//check for associating words
	if (OPT.enableAssociateMap && mRecitingWord.size() <= 20)
	{
		//cout<<"WordManager::selectCandidatesType-->assoWordList.size()="<<assoWordList.size()<<endl;
		QVector<AssociateWord>& assoWordList = mAssociateMap[mLastWordID];
		if (!assoWordList.empty())
		{
			int lastDiffLevel = mWordsMap[mLastWordID].getDifficultyLevel();
			bool passLastTest = mWordsMap[mLastWordID].passLastTest();
			if (mAssociateRemainCycle>0)
			{
				mAssociateRemainCycle--;
				mCurCandidatesType = WS_Associate;
				cout<<"WordManager::selectCandidatesType-->candidate type is WS_Associate."<<endl;
				return;
			}
			else if  (lastDiffLevel > 60 && !passLastTest)
			{
				mCurCandidatesType = WS_Associate;
				mAssociateRemainCycle = lastDiffLevel/30;
				mAssociateTempVector.clear();
				mAssociateRoot = mWordsMap[mLastWordID].getEnglish();
				for (int i=0; i<assoWordList.size(); i++)
				{
					if (mWordsVector.indexOf(assoWordList[i].id) == -1)
						mAssociateTempVector.push_back(assoWordList[i]);
				}

				if (!mAssociateTempVector.empty())
				{
					cout<<"WordManager::selectCandidatesType-->candidate type is associate."<<endl;
					return;
				}
				else
				{
					cout<<"WordManager::selectCandidatesType-->No associate word available."<<endl;
				}
			}
		}
	}

	//WLOG<<"WordManager::selectCandidatesType-->mReviewCycle = "<<mReviewCycle<<endl;
	//check for reviewing words
	if (mReviewCycle > 0)
		mReviewCycle--;

	if (mReviewCycle == 0)
	{
		cout<<"WordManager::selectCandidatesType()-->mReviewCycle == 0. Need review."<<endl;
		setReviewFrequency();
		mCurCandidatesType = WS_Reviewing;
		cout<<"WordManager::selectCandidatesType-->candidate type is reviewing"<<endl;
		return;
	}
	else
	{
		cout<<"WordManager::selectCandidatesType()-->mReviewCycle = "<< mReviewCycle <<". Do not need review."<<endl;
		mCurCandidatesType = WS_Reciting;
		cout<<"WordManager::selectCandidatesType-->candidate type is WS_Reciting"<<endl;
		return;
	}

	mCurCandidatesType = WS_Reciting;
	cout<<"WordManager::selectCandidatesType-->candidate type is WS_Reciting"<<endl;
}

void WordManager::setReviewFrequency()
{
	if (mReviewingWord.size() != 0 && mRecitingWord.size() != 0)
	{
		mReviewFrequency = int(float(mRecitingWord.size())/float(mReviewingWord.size())*50);
		if (mRecitingWord.size() > 20)
			mReviewFrequency = 20;
		else if (mRecitingWord.size() > 15)
			mReviewFrequency = 15;

		if (mReviewFrequency <2)
			mReviewFrequency = 2;
	}
	else
	{
		//previous algorithm
		if (mReviewingWord.size() < 5)
			mReviewFrequency = 30;

		if (mReviewingWord.size() >= 5 && mReviewingWord.size() <20)
			mReviewFrequency = 20;

		if (mReviewingWord.size() >= 20 && mReviewingWord.size() < 50)
			mReviewFrequency = 10;

		if (mReviewingWord.size() >= 50)
			mReviewFrequency = 5;
	}

	WLOG<<"WordManager::setReviewFrequency-->mReviewFrequency is set to: "<<mReviewFrequency<<endl;
	mReviewCycle = mReviewFrequency;
}

void WordManager::setReciteTestMode()
{
	cout<<"WordManager::setReciteTestMode"<<endl;

	Word* word = getCurWord();
	//cout<<"WordManager::setReciteTestMode-->word="<<word<<endl;
	if (word)
	{
		mReciteTestMethod = word->selectTestMethod();
		OPT.curTestMethod = mReciteTestMethod;
		//cout<<"WordManager::setReciteTestMode-->mReciteTestMethod="<<mReciteTestMethod<<endl;
	}
	else
	{
		cout<<"WordManager::setReciteTestMode-->invalid word."<<endl;
		assert(0);
	}
}

TestMethod WordManager::getReciteTestMode()
{
	return mReciteTestMethod;
}

void WordManager::randomSelectWords(vector<int>&wordIDs, int count)
{
	wordIDs.clear();
	int curWordIndex = random(count);
	mCorrectChoiceIndex = curWordIndex;
	//cout<<"WordManager::randomSelectWords-->curWordIndex = "<<curWordIndex<<endl;

	QString queryCommand;
	queryCommand = "select id from history where is_active = 1; ";
	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);
	vector<int>candidates;

	srand((int)time(0));
	for (int i=0; i<count-1; i++)
	{
		candidates.clear();
		bool isCandidate = false;
		for (int j=0; j<DB.getRowCount(); j++)
		{
			int id = DB.getTableValue(j,"id").toInt();
			//cout<<"WordManager::randomSelectWords-->id = "<<id<<endl;
			if(id != mCurWordID)
			{
				isCandidate = true;
				for (int k=0; k<wordIDs.size(); k++)
				{
					if (wordIDs[k] == id)
					{
						isCandidate = false;
						break;
					}
				}

				if (isCandidate)
				{
					candidates.push_back(id);
					//isCandidate = false;
				}
			}
		}

		//cout<<"WordManager::randomSelectWords-->candidates.size() = "<<candidates.size()<<endl;
		int wordIndex =0+(int)(float(candidates.size())*rand()/(RAND_MAX+1.0));
		wordIDs.push_back(candidates[wordIndex]);
		//cout<<"WordManager::randomSelectWords-->wordIDs["<<i<<"]="<<wordIDs[i]<<endl;
	}

	vector<int>::iterator id_it;

	id_it = wordIDs.begin();
	id_it = wordIDs.insert (id_it+ curWordIndex, mCurWordID );
}

bool WordManager::checkChoice(int selectedChoice)
{
	mSelectedChoice = selectedChoice;
	if(selectedChoice == mCorrectChoiceIndex)
		return true;
	else
		return false;
}

void WordManager::updateStatusCount()
{
	QString queryCommand;

	mRecitingWord.clear();
	queryCommand =  QString("select id from history where last_reciting_status =%1 and is_active = 1 ;").arg(int(WS_Reciting));
	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);
	for (int i=0; i<DB.getRowCount(); i++)
		mRecitingWord.push_back(DB.getTableValue(i,"id").toInt());
	//cout<<"WordManager::updateStatusCount-->mRecitingWordCount = "<<mRecitingWordCount<<endl;

	mReviewingWord.clear();
	queryCommand =  QString("select id from history where last_reciting_status =%1 and is_active = 1 ;").arg(int(WS_Reviewing));
	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);
	for (int i=0; i<DB.getRowCount(); i++)
		mReviewingWord.push_back(DB.getTableValue(i,"id").toInt());
	//cout<<"WordManager::updateStatusCount-->mReviewingWordCount = "<<mReviewingWordCount<<endl;

	mUntouchedWord.clear();
	queryCommand =  QString("select id from history where last_reciting_status =%1 and is_active = 1 ;").arg(int(WS_Untouched));
	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);
	for (int i=0; i<DB.getRowCount(); i++)
		mUntouchedWord.push_back(DB.getTableValue(i,"id").toInt());
	//cout<<"WordManager::updateStatusCount-->mUntouchedWordCount = "<<mUntouchedWordCount<<endl;

	mFinishedWord.clear();
	queryCommand =  QString("select id from history where last_reciting_status =%1 and is_active = 1 ;").arg(int(WS_Finished));
	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);
	for (int i=0; i<DB.getRowCount(); i++)
		mFinishedWord.push_back(DB.getTableValue(i,"id").toInt());
	//cout<<"WordManager::updateStatusCount-->mFinishedWordCount = "<<mFinishedWordCount<<endl;

}

void WordManager::browseWordByIndex(int index)
{
	if (index >= mWordsVector.size())
	{
		mCurWordIndex = mWordsVector.size()-1;
		mCurWordID = mWordsVector[mCurWordIndex];
	}
	else if(index < 0)
	{
		mCurWordIndex = 0;
		mCurWordID = mWordsVector[mCurWordIndex];
	}
	else
	{
		mCurWordIndex = index;
		mCurWordID = mWordsVector[mCurWordIndex];
	}
	getCurWordRecitingInfo();
}

void WordManager::getCurWordRecitingInfo()
{
	//cout<<"WordManager::getCurWordRecitingInfo"<<endl;
	QString queryCommand;

	queryCommand.clear();
	vector<QString>recitingInfo;
	recitingInfo.push_back("difficulty_level");
	recitingInfo.push_back("importance_level");
	recitingInfo.push_back("last_reciting_status");
	recitingInfo.push_back("recite_date_history");
	recitingInfo.push_back("memory_level");
	recitingInfo.push_back("memory_level_delta");
	queryCommand.append("select ");
	for(int i=0; i<recitingInfo.size()-1; i++)
		queryCommand.append(recitingInfo[i]).append(",");
	queryCommand.append(recitingInfo[recitingInfo.size()-1]);
	queryCommand.append(QString(" from history where id = %1").arg(mCurWordID));
	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);

	int difficulty_level = DB.getTableValue(0,"difficulty_level").toInt();
	int importance_level = DB.getTableValue(0,"importance_level").toInt();
	int last_reciting_status = DB.getTableValue(0,"last_reciting_status").toInt();
	QString recite_date_history = DB.getTableValue(0,"recite_date_history");
	int memory_level = DB.getTableValue(0,"memory_level").toInt();
	int memory_level_delta = DB.getTableValue(0,"memory_level_delta").toInt();

	mWordsMap[mCurWordID].setMemoryLevel(memory_level);
	mWordsMap[mCurWordID].setMemoryLevelDelta(memory_level_delta);
	mWordsMap[mCurWordID].setDifficultyLevel(difficulty_level);
	mWordsMap[mCurWordID].setImportanceLevel(importance_level);
	mWordsMap[mCurWordID].setWordStatus(last_reciting_status);
	mWordsMap[mCurWordID].setReciteDateHistory(recite_date_history);
}

void WordManager::updateCurWordRecitingInfo()
{
	cout<<"WordManager::updateCurWordRecitingInfo"<<endl;
	QString queryCommand;

	int memory_level =  mWordsMap[mCurWordID].getMemoryLevel();
	int memory_level_delta = mWordsMap[mCurWordID].getMemoryLevelDelta();
	int difficulty_level = mWordsMap[mCurWordID].getDifficultyLevel();
	int importance_level = mWordsMap[mCurWordID].getImportanceLevel();
	//cout<<"WordManager::updateCurWordRecitingInfo-->importance_level = "<<importance_level<<endl;
	int frozen_remain_cycle = mWordsMap[mCurWordID].getFrozenRemainCycle();
	WordStatus last_reciting_status = mWordsMap[mCurWordID].getWordStatus();
	QString recite_date_history = mWordsMap[mCurWordID].getReciteDateHistoryString();
	//cout<<"WordManager::updateCurWordRecitingInfo-->last_reciting_status="<<last_reciting_status.toUtf8().data()<<endl;
	//float grossPassRate = mWordsMap[mCurWordID].getGrossPassRate();
	//QString remember_forget_history = mWordsMap[mCurWordID]->getReciteHistoryString();

	DB.execQuery("BEGIN", QueryT_Write, DB.mUsrMain);
	queryCommand.clear();
	queryCommand.append(QString("update history set memory_level =%1 ").arg(memory_level));
	queryCommand.append(QString(" where id = %1 ").arg(mCurWordID));
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);

	queryCommand.clear();
	queryCommand.append(QString("update history set memory_level_delta=%1 ").arg(memory_level_delta));
	queryCommand.append(QString(" where id = %1 ").arg(mCurWordID));
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);

	queryCommand.clear();
	queryCommand.append(QString("update history set difficulty_level=%1 ").arg(difficulty_level));
	queryCommand.append(QString(" where id = %1 ").arg(mCurWordID));
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);

	queryCommand.clear();
	queryCommand.append(QString("update history set importance_level=%1 ").arg(importance_level));
	queryCommand.append(QString(" where id = %1 ").arg(mCurWordID));
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);

	queryCommand.clear();
	queryCommand.append(QString("update history set frozen_remain_cycle=%1 ").arg(frozen_remain_cycle));
	queryCommand.append(QString(" where id = %1 ").arg(mCurWordID));
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);

	queryCommand.clear();
	queryCommand.append(QString("update history set last_reciting_status=%1 ").arg(int(last_reciting_status)));
	queryCommand.append(QString(" where id = %1 ").arg(mCurWordID));
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);

	queryCommand.clear();
	queryCommand.append(QString("update history set recite_date_history = '%1' ").arg(recite_date_history));
	queryCommand.append(QString(" where id = %1 ").arg(mCurWordID));
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);

	/*queryCommand.clear();
	queryCommand.append(QString("update history set gross_pass_rate=%1 ").arg(grossPassRate));
	queryCommand.append(QString(" where id = %1 ").arg(mCurWordID));
	DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);*/
	DB.execQuery("COMMIT", QueryT_Write, DB.mUsrMain);
}

void WordManager::updateWordFrozenInfo()
{
	WLOG<<"WordManager::updateWordFrozenInfo"<<endl;
	//cout<<"WordManager::updateWordFrozenInfo"<<endl;
	QString queryCommand;

	int wordCount = 0;
	QString status;
	//cout<<"WordManager::updateWordFrozenInfo-->mCurCandidatesType="<<mCurCandidatesType<<endl;
	if (mCurCandidatesType == WS_Untouched)
	{
		mCurCandidatesType = WS_Reviewing;
		updateWordFrozenInfo();
		mCurCandidatesType = WS_Reciting;
		updateWordFrozenInfo();
	}

	queryCommand.clear();
	queryCommand = QString(
			"select id,frozen_remain_cycle from history "
			"where is_active = 1 "
			"and last_reciting_status = %1 "
			//"and frozen_remain_cycle > 0 "
			";").arg(int(mCurCandidatesType));
	DB.execQuery(queryCommand, QueryT_Read, DB.mUsrMain);

	//cout<<"WordManager::updateWordFrozenInfo-->Frozen word count is "<<wordCount<<endl;
	int frozenWordCount = 0;
	DB.quickUpdate("BEGIN");
	for(int i=0; i<DB.getRowCount(); i++)
	{
		//cout<<"i="<<i<<endl;
		int id = DB.getTableValue(i,"id").toInt();
		int frozen_remain_cycle = DB.getTableValue(i,"frozen_remain_cycle").toInt();
		Word* word = getWord(id);
		QString English;
		if(word)
			English = word->getEnglish();
		else
		{
			cout<<"WordManager::updateWordFrozenInfo-->Has inadequate information about word(id="<<id<<")"<<endl;
			QCoreApplication::quit();
		}

		if(frozen_remain_cycle > 1)
		{
			frozen_remain_cycle--;
			frozenWordCount++;
			//WLOG<<"WordManager::updateWordFrozenInfo-->word["<<id<<"] "
			//		<<English.toUtf8().data()<<" is FROZEN("<<frozen_remain_cycle<<")."<<endl;
		}
		else if(frozen_remain_cycle == 1)
		{
			frozen_remain_cycle = 0;
			//WLOG<<"WordManager::updateWordFrozenInfo-->word["<<id<<"] "
			//		<<English.toUtf8().data()<<" is UNFROZEN."<<endl;
		}
		else
			frozen_remain_cycle = 0;

		queryCommand.clear();
		queryCommand.append(QString("update history set frozen_remain_cycle = %1 ").arg(frozen_remain_cycle));
		queryCommand.append(QString(" where id = %1 ;").arg(id));
		//execQurey(queryCommand);
		DB.execQuery(queryCommand, QueryT_Write, DB.mUsrMain);
	}
	DB.quickUpdate("COMMIT");
	WLOG<<"WordManager::updateWordFrozenInfo-->Frozen word count is "<<frozenWordCount<<endl;
}

void WordManager::remember()
{
	WLOG<<"WordManager::remember"<<endl;
	mWordsMap[mCurWordID].remember();
	mTotalReciteCount++;
	//updateCurWordRecitingInfo();
}

void WordManager::forget()
{
	WLOG<<"WordManager::forget"<<endl;
	mWordsMap[mCurWordID].forget();
	mTotalReciteCount++;
	//updateCurWordRecitingInfo();
}

Word* WordManager::getCurWord()
{
	//cout<<"WordManager::getCurWord"<<endl;
	map<int, Word>::iterator word_it;
	word_it = mWordsMap.find(mCurWordID);
	if(word_it != mWordsMap.end())
	{
		//cout<<"WordManager::getCurWord-->word is = "<<word_it->second.getID()<<endl;
		return &(word_it->second);
	}
	else
		return NULL;
}

Word* WordManager::getWord(int id)
{
	map<int, Word>::iterator word_it;
	word_it = mWordsMap.find(id);
	if(word_it != mWordsMap.end())
		return &(word_it->second);
	else
		return NULL;
}

Word* WordManager::getWordByIndex(int index)
{
	int id = mWordsVector[index];
	return getWord(id);
}

int WordManager::random(int size)
{
	//srand((int)time(0));
	int i=0+(int)(float(size)*rand()/(RAND_MAX+1.0));
	return i;
}

bool WordManager::random(QVector<int>& results , int resultCount, int size, int base)
{
	cout<<"WordManager::random"<<endl;
	//vector<int>::iterator int_it;
	if(resultCount < size)
	{
		while(results.size() < resultCount)
		{
			int j=base+(int)(float(size)*rand()/(RAND_MAX+1.0));
			//int_it = std::find(results.begin(), results.end(), j);
			int index = results.indexOf(j);
			//if(int_it == results.end())
			if(index == -1)
			{
				results.push_back(j);
			}
		}
		return true;
	}
	else
		return false;
}

void WordManager::setCurWordId(int id)
{
	if (id >= 0 && id < mWordsMap.size())
		mCurWordID=id;
	else
	{
		cout<<"WordManager::setCurWordId-->word id is out of range!!!"<<endl;
		//assert(0);
		QCoreApplication::quit();
	}
}

int WordManager::getActiveWordCount()
{
	int count = mRecitingWord.size() + mReviewingWord.size() + mUntouchedWord.size() + mFinishedWord.size();
	return count;
}

int WordManager::getActiveWordTotalFinishedCount()
{
	int count = mReviewingWord.size() + mFinishedWord.size();
	return count;
}

void WordManager::initActiveWordTotalReciteCount()
{
	mTotalReciteCount = 0;
	for (int i=0; i<mWordsVector.size(); i++)
		mTotalReciteCount += mWordsMap[mWordsVector[i]].getTotalReciteCount();
}

int WordManager::getActiveWordTotalReciteCount()
{
	return mTotalReciteCount;
}

int WordManager::getWordCountByStatus(WordStatus status)
{
	switch(status)
	{
	case WS_Untouched:
		return mUntouchedWord.size();
	case WS_Finished:
		return mFinishedWord.size();
	case WS_Reciting:
		return mRecitingWord.size();
	case WS_Reviewing:
		return mReviewingWord.size();
	}
}

void WordManager::loadAssiciateMap()
{
	QString command;
	QString associateDB = QString("WordGroup");
	QString path = QString("./data/common/");
	if (!DB.openDB(path, associateDB))
	{
		cout<<"WordManager::loadAssiciateMap-->fail to open associate map database"<<endl;
		OPT.hasAssociateMap=false;
		return;
		//assert(0);
	}
	else
		OPT.hasAssociateMap=true;

	if (OPT.hasAssociateMap)
	{
		mAllWordMap.clear();
		mAssociateMap.clear();
		command = "select id,English,group_info from group_info;";
		DB.execQuery(command, QueryT_Read, associateDB);
		for (int i=0; i<DB.getRowCount(); i++)
		{
			int id = DB.getTableValue(i, "id").toInt();
			QString English = DB.getTableValue(i, "English");
			mAllWordMap[id] = English;
		}

		for (int i=0; i<DB.getRowCount(); i++)
		{
			int id= DB.getTableValue(i, "id").toInt();
			QString wordListString = DB.getTableValue(i, "group_info");
			QStringList wordList = wordListString.split(",", QString::SkipEmptyParts);
			for (int j=0; j<wordList.size(); j++)
			{
				//int assoID = wordList[j].toInt();
				if (!wordList[j].contains("[") || !wordList[j].contains("]"))
				{
					cout<<"WordManager::loadAssiciateMap-->invalid word record:"<<wordList[j].toUtf8().data()<<endl;
					assert(0);
				}

				int assoID = wordList[j].left(wordList[j].indexOf("[")).toInt();
				int conWei = wordList[j].mid(wordList[j].indexOf("[")+1, wordList[j].indexOf("]") - wordList[j].indexOf("[")-1).toInt();
				if (mAllWordMap[assoID].isEmpty())
				{
					cout<<"WordManager::loadAssiciateMap-->fail to get English from mAllWordMap. (id="<<assoID<<")"<<endl;
					assert(0);
				}

				mAssociateMap[id].push_back(AssociateWord(assoID, conWei, mAllWordMap[assoID]));
			}
			//cout<<"WordManager::loadAssiciateMap-->mAssociateMap["<<id<<"].size()="<<mAssociateMap[id].size()<<endl;
		}
	}
}

QStringList WordManager::getAssociateWords(int id, bool isActive)
{
	QStringList result;
	QVector<AssociateWord> finalWords;
	map<int, QVector<AssociateWord> >::iterator word_it;
	word_it = mAssociateMap.find(id);
	if (word_it != mAssociateMap.end())
	{
		QVector<AssociateWord>& allAsso = word_it->second;
		for (int i=0; i<allAsso.size(); i++)
		{
			if (isActive)
			{
				if (mWordsVector.indexOf(allAsso[i].id) != -1)
				{
					QString English = allAsso[i].English;
					int conWeight = allAsso[i].conWeight;
					result<<English+QString("[%1]").arg(conWeight);
				}
			}
			else
			{
				QString English = allAsso[i].English;
				int conWeight = allAsso[i].conWeight;
				result<<English+QString("[%1]").arg(conWeight);
				//finalWords.push_back(allAsso[i]);
			}
		}
	}

	/*QString command;
	if (!finalWords.isEmpty())
	{
		command = QString("select id,English from words where ");
		for (int i=0; i<finalWords.size()-1; i++)
			command.append(QString(" id=%1 or ").arg(finalWords[i].id));
		command.append(QString(" id=%1; ").arg(finalWords[finalWords.size()-1].id));
	}
	DB.quickGet(command);

	for (int i=0; i<DB.getRowCount(); i++)
	{
		int id = DB.getTableValue(i, "id").toInt();
		int conWei=0;
		for (int j=0; j<finalWords.size(); j++)
		{
			if (id == finalWords[j].id)
				conWei = finalWords[j].conWeight;
		}
		result<<DB.getTableValue(i, "English")+QString("[%1]").arg(conWei);
	}*/

	return result;
}

